#ifndef DTLS_HPP
#define DTLS_HPP
#include "logger.hpp"
#include "udp_client.hpp"
#include "srtp_session.hpp"

#include <stdint.h>
#include <stddef.h>
#include <string>
#include <vector>
#include <map>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/ec.h>

namespace cpp_streamer
{
#define SSL_READ_BUFFER_SIZE 65536

#define DTLS_MTU 1350

#define SRTP_MASTER_KEY_LENGTH   16
#define SRTP_MASTER_SALT_LENGTH  14
#define SRTP_MASTER_LENGTH (SRTP_MASTER_KEY_LENGTH + SRTP_MASTER_SALT_LENGTH)

#define DTLS_SRTP_MASTER_KEY_LEN (16 + 16 + 14 + 14)
#define RTC_ELAPSED(x, y) ((y) - (x))

#define SRTP_AESGCM_256_MASTER_KEY_LENGTH   32
#define SRTP_AESGCM_256_MASTER_SALT_LENGTH  12
#define SRTP_AESGCM_256_MASTER_LENGTH (SRTP_AESGCM_256_MASTER_KEY_LENGTH + SRTP_AESGCM_256_MASTER_SALT_LENGTH)

#define SRTP_AESGCM_128_MASTER_KEY_LENGTH   16
#define SRTP_AESGCM_128_MASTER_SALT_LENGTH  12
#define SRTP_AESGCM_128_MASTER_LENGTH  (SRTP_AESGCM_128_MASTER_KEY_LENGTH + SRTP_AESGCM_128_MASTER_SALT_LENGTH)

enum DTLSState {
    DTLS_STATE_NONE,

    /* Whether DTLS handshake is finished. */
    DTLS_STATE_FINISHED,
    /* Whether DTLS session is closed. */
    DTLS_STATE_CLOSED,
    /* Whether DTLS handshake is failed. */
    DTLS_STATE_FAILED,
};

typedef enum
{
    DTLS_NEW = 1,
    DTLS_CONNECTING,
    DTLS_CONNECTED,
    DTLS_FAILED,
    DTLS_CLOSED
} DTLS_STATE;

typedef enum
{
    ROLE_NONE = 0,
    ROLE_AUTO = 1,
    ROLE_CLIENT,
    ROLE_SERVER
} DTLS_ROLE;

typedef enum
{
    NONE                    = 0,
    AES_CM_128_HMAC_SHA1_80 = 1,
    AES_CM_128_HMAC_SHA1_32,
    AEAD_AES_256_GCM,
    AEAD_AES_128_GCM
} crypto_suite_enum;

typedef enum
{
    FINGER_NONE = 0,
    FINGER_SHA1 = 1,
    FINGER_SHA224,
    FINGER_SHA256,
    FINGER_SHA384,
    FINGER_SHA512
}finger_print_algorithm_enum;

typedef struct srtp_crypto_suite_map_s
{
    crypto_suite_enum crypto_suite;
    std::string name;
} srtp_crypto_suite_map;

typedef struct finger_print_info_s
{
    finger_print_algorithm_enum algorithm = FINGER_NONE;
    std::string value;
} finger_print_info;

extern std::vector<srtp_crypto_suite_map> srtp_crypto_suite_vec;
extern std::map<std::string, finger_print_algorithm_enum> string2finger_print_algorithm;
extern std::map<finger_print_algorithm_enum, std::string> finger_print_algorithm2String;

typedef enum
{
    ICE_NET_UNKNOWN,
    ICE_TCP,
    ICE_UDP
} ICE_NET_TYPE;

class IceInfo
{
public:
    IceInfo() {}
    ~IceInfo() {}

    bool operator==(const IceInfo& info) {
        return (info.net_type == this->net_type) 
            && (info.hostip == this->hostip) 
            && (info.port == this->port);
    }
public:
    ICE_NET_TYPE net_type;
    std::string hostip;
    uint16_t port;
};

class PeerConnection;

class RtcDtls
{
public:
    Logger* logger_      = nullptr;
    EVP_PKEY* dtls_pkey_ = nullptr;
    EC_KEY* dtls_eckey_  = NULL;
    X509 *dtls_cert_     = nullptr;
    std::string fg_algorithm_;
    std::string fingerprint_;

    SSL_CTX* ctx_ = nullptr;
    SSL *dtls_    = nullptr;
    BIO *bio_in_  = nullptr;
    int dtls_arq_packets_ = 0;
    uint8_t dtls_last_content_type_   = 0;
    uint8_t dtls_last_handshake_type_ = 0;
    enum DTLSState state_  = DTLS_STATE_NONE;
    bool dtls_done_for_us_ = false;
    bool dtls_closed_      = false;
    bool dtls_srtp_key_exported_ = false;
    /**
     * This represents the material used to build the SRTP master key. It is
     * generated by DTLS and has the following layout:
     *          16B         16B         14B             14B
     *      client_key | server_key | client_salt | server_salt
     */
    uint8_t dtls_srtp_materials_[DTLS_SRTP_MASTER_KEY_LEN * 2];

    int64_t rtc_starttime_ = 0;
    int64_t rtc_dtls_time_ = 0;
    int64_t dtls_handshake_starttime_ = 0;
    int64_t dtls_handshake_endtime_   = 0;

    char error_message[512];

public:
    UdpClient* udp_client_ = nullptr;
    UdpTuple remote_address_;

public:
    std::string local_fragment_;
    std::string local_pwd_; 

public:
    std::string remote_fragment_;
    std::string remote_pwd_;

public:
    std::vector<IceInfo> ice_infos;

public:
    // The DtlsSrtp ciphers
    std::string srtp_ciphers_;

private:
    PeerConnection* pc_;

public:
    RtcDtls(PeerConnection* pc, Logger* logger);
    ~RtcDtls();

public:
    static bool IsDtls(const uint8_t* data, size_t len);

public:
    int SslContextInit();
    int DtlsStart();

public:
    void OnDtlsData(uint8_t* data, int size);
    int OnWrite(uint8_t* data, int size);
    int OnState(enum DTLSState state, const char* type, const char* desc);

private:
    int GenPrivateKey();
    int GenPrivateCert();
    int InitContext();
    int SetupSRtp(CRYPTO_SUITE_ENUM crypto_suite = CRYPTO_SUITE_AES_CM_128_HMAC_SHA1_80);
};

}

#endif


